package io.itit.ecp.admin.provide.helper.datarangelevel;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import io.itit.ecp.admin.common.constants.EcpAdminConstants;
import io.itit.ecp.admin.common.enums.IDataRangeEnums;
import io.itit.ecp.admin.common.utils.EcpAdminUtils;
import io.itit.ecp.admin.provide.helper.CarbonAbsFactorHelper;
import io.itit.ecp.admin.provide.helper.DataRangeHelper;
import io.itit.ecp.admin.server.entity.DataRangeSceneEntity;
import io.itit.ecp.admin.server.entity.SceneRoomEntity;
import io.itit.ecp.admin.server.service.IDataRangeSceneService;
import io.itit.ecp.admin.server.service.ISceneRoomService;
import io.itit.grass.common.exception.GrassException;
import io.itit.grass.common.server.helper.RedisHelper;
import io.itit.grass.web.wall.utils.ShiroUtils;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Component
public abstract class AbsDataRangeLevelHelper {

    @Resource
    protected RedisHelper redisHelper;

    @Resource
    protected ISceneRoomService sceneRoomService;

    @Resource
    protected IDataRangeSceneService dataRangeSceneService;

    public abstract IDataRangeEnums.RangeLevel rangeLevel();

    public abstract List<SceneRoomEntity> listByScene(Collection<String> sceneIds);

    public abstract void processDataRange(List<SceneRoomEntity> roomList);

    protected String getUserId() {
        return ShiroUtils.getUserId();
    }

    public abstract Expression getSqlSegment(Table table, Map<String, List<String>> tableMap);

    public Expression getSqlSegment(Table table, Expression where, String mappedStatementId, Map<String, List<String>> tableMap) {
        // 碳汇表需要做特殊处理，如果数据范围级别不是所有级别或者项目级别的权限就不让查询
        if (EcpAdminConstants.ECP_TT_CARBON_SINK_DATA.equalsIgnoreCase(table.getName())
                && IDataRangeEnums.RangeLevel.getLevel(DataRangeHelper.i().getDataRangeLevel()) > 2) {
            return buildNotEquals();
        }
        return getSqlSegment(table, tableMap);
    }

    public void processDataRange(String dataRangeId) {

        List<String> sceneIds = dataRangeSceneService.lambdaQuery()
                .eq(DataRangeSceneEntity::getDataRangeId, dataRangeId)
                .list()
                .stream()
                .map(DataRangeSceneEntity::getSceneId)
                .distinct()
                .collect(Collectors.toList());
        processDataRange(listByScene(sceneIds));
        // 强行把当前范围级别的场景id设置到缓存
        putSceneList(getUserId(), rangeLevel().rangeLevel, sceneIds);
    }

    protected List<SceneRoomEntity> listByProject(Collection<String> projectIds) {
        return list(projectIds, null, null, null, null);
    }

    protected List<SceneRoomEntity> listByEdifice(Collection<String> edificeIds) {
        return list(null, edificeIds, null, null, null);
    }

    protected List<SceneRoomEntity> listByFloor(Collection<String> floorIds) {
        return list(null, null, floorIds, null, null);
    }

    protected List<SceneRoomEntity> listByRoom(Collection<String> roomIds) {
        return list(null, null, null, roomIds, null);
    }

    protected List<SceneRoomEntity> listByCompany(Collection<String> companyIds) {
        return list(null, null, null, null, companyIds);
    }

    protected List<SceneRoomEntity> list(Collection<String> projectIds, Collection<String> edificeIds, Collection<String> floorIds,
                                         Collection<String> roomIds, Collection<String> companyIds) {
        return sceneRoomService.lambdaQuery()
                .in(CollectionUtils.isNotEmpty(roomIds), SceneRoomEntity::getId, roomIds)
                .in(CollectionUtils.isNotEmpty(companyIds), SceneRoomEntity::getSceneCompanyId, companyIds)
                .in(CollectionUtils.isNotEmpty(projectIds), SceneRoomEntity::getSceneProjectId, projectIds)
                .in(CollectionUtils.isNotEmpty(edificeIds), SceneRoomEntity::getSceneEdificeId, edificeIds)
                .in(CollectionUtils.isNotEmpty(floorIds), SceneRoomEntity::getSceneFloorId, floorIds)
                .list();
    }

    public void putRangeLevelProject(String userId, List<SceneRoomEntity> roomList) {
        putSceneList(userId, IDataRangeEnums.RangeLevel.rangeLevelProject.rangeLevel,
                roomList.stream().map(SceneRoomEntity::getSceneProjectId)
                        .filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList()));
    }

    public void putRangeLevelEdifice(String userId, List<SceneRoomEntity> roomList) {
        putSceneList(userId, IDataRangeEnums.RangeLevel.rangeLevelEdifice.rangeLevel,
                roomList.stream().map(SceneRoomEntity::getSceneEdificeId)
                        .filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList()));
    }

    public void putRangeLevelFloor(String userId, List<SceneRoomEntity> roomList) {
        putSceneList(userId, IDataRangeEnums.RangeLevel.rangeLevelFloor.rangeLevel,
                roomList.stream().map(SceneRoomEntity::getSceneFloorId)
                        .filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList()));
    }

    public void putRangeLevelRoom(String userId, List<SceneRoomEntity> roomList) {
        putSceneList(userId, IDataRangeEnums.RangeLevel.rangeLevelRoom.rangeLevel,
                roomList.stream().map(SceneRoomEntity::getId).distinct().collect(Collectors.toList()));
    }

    public void putRangeLevelCompany(String userId, List<SceneRoomEntity> roomList) {
        putSceneList(userId, IDataRangeEnums.RangeLevel.rangeLevelCompany.rangeLevel,
                roomList.stream().map(SceneRoomEntity::getSceneCompanyId)
                        .filter(StringUtils::isNotEmpty).distinct().collect(Collectors.toList()));
    }

    public Expression buildProjectExpression(String userId, Table table, List<String> columnList) {
        return buildExpression(userId, table, columnList, IDataRangeEnums.RangeLevel.rangeLevelProject);
    }

    public Expression buildEdificeExpression(String userId, Table table, List<String> columnList) {
        return buildExpression(userId, table, columnList, IDataRangeEnums.RangeLevel.rangeLevelEdifice);
    }

    public Expression buildFloorExpression(String userId, Table table, List<String> columnList) {
        return buildExpression(userId, table, columnList, IDataRangeEnums.RangeLevel.rangeLevelFloor);
    }

    public Expression buildRoomExpression(String userId, Table table, List<String> columnList) {
        return buildExpression(userId, table, columnList, IDataRangeEnums.RangeLevel.rangeLevelRoom);
    }

    public Expression buildCompanyExpression(String userId, Table table, List<String> columnList) {
        // 只限制公司表才拼接公司的条件查询
        if (!IDataRangeEnums.RangeLevel.rangeLevelCompany.table.equals(table.getName())) {
            return null;
        }
        return buildExpression(userId, table, columnList, IDataRangeEnums.RangeLevel.rangeLevelCompany);
    }


    public void putSceneList(String userId, String rangeLevel, List<String> sceneList) {
        if (CollectionUtils.isEmpty(sceneList)) {
            redisHelper.delete(CarbonAbsFactorHelper.buildKey(EcpAdminConstants.DATA_RANGE_SCENE, rangeLevel), userId);
        } else {
            redisHelper.put(CarbonAbsFactorHelper.buildKey(EcpAdminConstants.DATA_RANGE_SCENE, rangeLevel), userId, EcpAdminUtils.joiner(sceneList));
        }
    }

    public List<String> getSceneList(String userId, String rangeLevel) {
        return EcpAdminUtils.parse(redisHelper.get(CarbonAbsFactorHelper.buildKey(EcpAdminConstants.DATA_RANGE_SCENE, rangeLevel), userId));
    }

    protected static Expression buildExpression(Expression... exps) {
        List<Expression> expList = Arrays.stream(exps)
                .filter(Objects::nonNull).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(expList)) {
            return null;
        }
        if (expList.size() == 1) {
            return expList.get(0);
        }
        if (expList.size() == 2) {
            return new AndExpression(expList.get(0), expList.get(1));
        }
        if (expList.size() == 3) {
            return new AndExpression(expList.get(0), new AndExpression(expList.get(1), expList.get(2)));
        }
        if (expList.size() == 4) {
            return new AndExpression(new AndExpression(expList.get(0), expList.get(1)), new AndExpression(expList.get(2), expList.get(3)));
        }
        if (expList.size() == 5) {
            return new AndExpression(new AndExpression(expList.get(0), expList.get(1)), new AndExpression(expList.get(2), new AndExpression(expList.get(3), expList.get(4))));
        }
        if (expList.size() == 6) {
            return new AndExpression(new AndExpression(expList.get(0), expList.get(1)), new AndExpression(new AndExpression(expList.get(2), expList.get(3)), new AndExpression(expList.get(4), expList.get(5))));
        }
        throw new GrassException("需要拼接的数据范围条件过多，请检查是否合理！");
    }

    /**
     * 构建条件
     *
     * @param userId     用户id
     * @param table      表
     * @param columnList 表字段
     * @param rangeLevel 数据范围级别
     * @return
     */
    protected Expression buildExpression(String userId, Table table, List<String> columnList, IDataRangeEnums.RangeLevel rangeLevel) {
        // 如果当前查询和数据范围表相同 就把拼接的字段替换成id
        boolean isSame = StringUtils.equals(table.getName(), rangeLevel.table);

        // 如果当前表没有这个字段 就返回null
        if (!columnList.contains(rangeLevel.column) && !isSame) {
            return null;
        }

        List<String> ids = getSceneList(userId, rangeLevel.rangeLevel);
        if (CollectionUtils.isEmpty(ids)) {
            return null;
        }
        // 条件
        InExpression inExpression = new InExpression();
        // 字段名
        inExpression.setLeftExpression(buildColumn(table.getAlias(), isSame ? EcpAdminConstants.ID : rangeLevel.column));
        // 匹配对象值
        ExpressionList expressionList = new ExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList()));
        inExpression.setRightItemsList(expressionList);
        return inExpression;
    }

    /**
     * 构建条件
     *
     * @return 带表别名字段
     */
    protected static EqualsTo buildNotEquals() {
        // 条件
        EqualsTo equalsTo = new EqualsTo();
        // 字段名
        equalsTo.setLeftExpression(buildColumn(null, "1"));
        // 匹配对象值
        equalsTo.setRightExpression(new StringValue("2"));
        return equalsTo;
    }

    /**
     * 构建Column
     *
     * @param alias      表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    protected static Column buildColumn(Alias alias, String columnName) {
        if (Objects.nonNull(alias) && StringUtils.isNotEmpty(alias.getName())) {
            columnName = alias.getName() + "." + columnName;
        }
        return new Column(columnName);
    }
}
